local _M = { _VERSION = "0.1" }

local ngx = _G.ngx

local cjson = require "cjson.safe"
      cjson.encode_max_depth(32)
      cjson.decode_max_depth(32)

local ERROR_CODE = {
  FAILURE = 'FAILURE',
}

do
  local function _trigger_on_exit()
    if ngx.ctx._ngx_before_exit then
      local listeners = ngx.ctx._ngx_before_exit
      for i = #listeners, 1, -1 do
        local proc = listeners[i]
        proc()
      end
    end
  end


  function _M.on_exit(proc)
    assert("function"==type(proc))

    if not ngx.ctx._ngx_before_exit then
      ngx.ctx._ngx_before_exit = { proc }
    else
      -- NOTE: For readability sake the ngx.ctx._ngx_before_exit using table.insert() to add items
      --   instead of indexer operation. We expect the operation will be used in 
      -- the following statement is equivalent, 
      table.insert(ngx.ctx._ngx_before_exit, proc)
    end
  end


  function _M.failure(code, message)
    if message == "" then
      message = nil
    end

    ngx.status = ngx.HTTP_BAD_REQUEST
    ngx.header.content_type = 'application/json; charset=utf-8'
    ngx.print(cjson.encode({
      message     = code,
      description = message,
      timestamp   = ngx.now() * 1000,
    }))
    _trigger_on_exit()
    ngx.exit(ngx.OK)
  end


  function _M.success(result)
    if 'table' == type(result) then
      rawset(result, "timestamp", ngx.now() * 1000)

      ngx.header.content_type = 'application/json; charset=utf-8'
      ngx.print(cjson.encode(result))
    else
      ngx.print(result)
    end
    _trigger_on_exit()
    ngx.exit(ngx.OK)
  end


  function _M.error(err)
    if "table" == type(err) then
      return _M.failure(err.message, err.description)
    end

    local err_code = ERROR_CODE.FAILURE
    if type(err) == "string" then
      local m, _ = ngx.re.match(err, "^[A-Z_]+$", "oj")
      if m  and  next(m) then
        return _M.failure(err)
      end
    end
    return _M.failure(err_code, err)
  end


  function _M.redis_error_reply(err)
    return _M.error(err)
  end
end
return _M
